<?php
/**
 * tp项目中使用的函数，他依赖前面定义的变量
 */





/**
 * 功能：加载运行时所需要的文件,并负责自动目录生成(依赖build_app_dir check_runtime函数)
 */
function load_runtime_file() {

    // 加载系统基础函数库
    require TPCORE_PATH.'Common/common.php';

    // 读取核心编译文件列表
    $list = array(
        CORE_PATH.'Core/Think.class.php',            //tp核心处理类
        CORE_PATH.'Core/ThinkException.class.php',  // 异常处理类
        CORE_PATH.'Core/Behavior.class.php',        //行为处理类
    );
    // 加载核心这些文件
    foreach ($list as $key=>$file){
        if(is_file($file))  require_cache($file);
    }

    // 加载系统类库别名定义
    alias_import(include TPFRAMEWORK.'Conf/alias.php');

    // 检查项目目录结构以项目lib路径为例，不存在则自动创建项目目录结构
    if(!is_dir(LIB_PATH)) {
        // 创建项目目录结构
        build_app_dir();
    }elseif(!is_dir(CACHE_PATH)){//检查项目缓存目录是否存在
        // 检查缓存目录
        check_runtime();
    }elseif(APP_DEBUG){  //如果开启调试模式，编译文件删除
        if(is_file(RUNTIME_FILE))   unlink(RUNTIME_FILE);
    }
}

/**
 * 功能：创建编译缓存 生成编译文件
 * 说明：将项目中定义的以及系统中的文件创建编译缓存，写到缓存文件中
 * 使用strip_whitespace去除注释和空白。
 * @param string $append 传入字符串
 */
function build_runtime_cache($append='') {
    $defs = get_defined_constants(TRUE);//能够打印出php中所有常量的关联数组
    $content    =  '$GLOBALS[\'_beginTime\'] = microtime(TRUE);';
    if(defined('RUNTIME_DEF_FILE')) { //是否定义外部常量的引入
        file_put_contents(RUNTIME_DEF_FILE,'<?php '.array_define($defs['user']));
        $content  .=  'require \''.RUNTIME_DEF_FILE.'\';';
    }else{
        $content  .= array_define($defs['user']); //将用户设置的常量定义编译到文件
    }

    // 读取核心编译文件列表
    $list = array(
        TPCORE_PATH.'Common/common.php',
        CORE_PATH.'Core/Think.class.php',
        CORE_PATH.'Core/ThinkException.class.php',
        CORE_PATH.'Core/Behavior.class.php',
    );
    foreach ($list as $file){
        $content .= compile($file);
    }
    // 系统行为扩展文件统一编译
    if(C('APP_TAGS_ON')) {
        $content .= build_tags_cache();
    }
    // 系统内别名定义编译
    $alias = include TPCORE_PATH.'Conf/alias.php';
    $content .= 'alias_import('.var_export($alias,true).');';

    // 编译框架默认语言包和配置参数以及加载时间,thinkphp 核心文件运行
    $content .= $append."\nL(".var_export(L(),true).");C(".var_export(C(),true).');G(\'loadTime\');Think::Start();';
    file_put_contents(RUNTIME_FILE,strip_whitespace('<?php '.$content));
}

/**
 * 功能：编译系统行为扩展类库
 * 说明：先获取定义的类库，通过循环这里支持二维数组
 * 例如类库：$return=['app_begin'=>array('CronRun'),'app_begin'=>array('CheckLang'),]
 * 如果遍历二维数组值，如果是整数，将值按照规范。来获取该扩展类文件中的内容。如果是字符串，直接获取文件中的内容
 * @return string
 */
function build_tags_cache() {
    $tags = C('extends');
    $content = '';
    foreach ($tags as $tag=>$item){
        foreach ($item as $key=>$name) {
            $content .= is_int($key)?compile(CORE_PATH.'Behavior/'.$name.'Behavior.class.php'):compile($name);
        }
    }
    return $content;
}
/**
 * 功能：tp框架创建项目目录文件
 *
 */
function build_app_dir() {
    // 没有创建项目目录的话自动创建
    if(!is_dir(APP_PATH)) mk_dir(APP_PATH,0777);//没有创建过项目目录自动创建并赋予读写权限
    if(is_writeable(APP_PATH)) {  //文件可写S
        $dirs  = array(
            LIB_PATH,
            RUNTIME_PATH,
            CONF_PATH,
            COMMON_PATH,
            LANG_PATH,
            CACHE_PATH,
            TMPL_PATH,
            TMPL_PATH.C('DEFAULT_THEME').'/',
            LOG_PATH,
            TEMP_PATH,
            DATA_PATH,
            LIB_PATH.'Model/',
            LIB_PATH.'Action/',
            LIB_PATH.'Behavior/',
            LIB_PATH.'Widget/',
        );
        foreach ($dirs as $dir){       //判断是否文件存在，不存在则创建
            if(!is_dir($dir))  mk_dir($dir,0777);
        }
        // 目录安全写入
        defined('BUILD_DIR_SECURE') or define('BUILD_DIR_SECURE',false);
        //允许目录写入
        if(BUILD_DIR_SECURE) {
            //生成默认的空白文件
            defined('DIR_SECURE_FILENAME') or define('DIR_SECURE_FILENAME','index.html');

            defined('DIR_SECURE_CONTENT') or define('DIR_SECURE_CONTENT','hello  这是tonicgb生成的');

            //默认文件的内容
            $content = DIR_SECURE_CONTENT;// 自动写入目录安全文件
            $a = explode(',', DIR_SECURE_FILENAME);//支持生成多个文件空白文件
            foreach ($a as $filename){
                foreach ($dirs as $dir)      //循环向文件目录中，写入默认文件的内容
                    file_put_contents($dir.$filename,$content);
            }
        }
        // 写入配置文件
        if(!is_file(CONF_PATH.'config.php')) //判断文件是否存在
            file_put_contents(CONF_PATH.'config.php',"<?php\nreturn array(\n\t//'配置项'=>'配置值'\n);\n?>"); //向配置文件中写入默认配置信息

        // 未找到文件
        if(!is_file(LIB_PATH.'Action/IndexAction.class.php'))
            build_first_action(); // 写入测试Action
    }else{
        header('Content-Type:text/html; charset=utf-8');
        exit('项目目录不可写，目录无法自动生成！<BR>请使用项目生成器或者手动生成项目目录~');
    }
}
/**
 * 功能：创建测试Action。并写入预备内容
 */
function build_first_action() {
    //获取预备模板内容
    $content = file_get_contents(TPCORE_PATH.'Tpl/default_index.tpl');
    //写到测试Action中
    file_put_contents(LIB_PATH.'Action/IndexAction.class.php',$content);
}

/**
 * 功能：将用户定义的常量，我们对常量的这个定义进行字符串拼接
 * 说明：根据传入的常量数组，生成常量的定义
 * @param $array
 * @param bool $check
 * @return string
 */
function array_define($array,$check=true) {
    $content = "\n";
    foreach ($array as $key => $val) {
        $key = strtoupper($key);//将用户定义的常量键名转大写
        if($check)   $content .= 'if(!defined(\'' . $key . '\')) '; //拼接常量判断
        if (is_int($val) || is_float($val)) {//正数或浮点数的值拼接define来设置
            $content .= "define('" . $key . "'," . $val . ");";
        } elseif (is_bool($val)) {//boolean类型的，我们使用true,flase字符串来拼接设置
            $val = ($val) ? 'true' : 'false';
            $content .= "define('" . $key . "'," . $val . ");";
        } elseif (is_string($val)) { //字符串类型的我们需要对字符串中的特殊字符进行转义
            $content .= "define('" . $key . "','" . addslashes($val) . "');";
        }
        $content    .= "\n";
    }
    return $content;
}
/**
 * 功能：检查是否存在缓存目录(Runtime) 如果不存在则自动创建
 * 说明：创建缓存目录->模板缓存->日志目录->数据缓存目录->数据文件目录
 * @return bool
 */
function check_runtime() {
    if(!is_dir(RUNTIME_PATH)) { //目录不存在
        mkdir(RUNTIME_PATH,0777);   //创建目录
    }elseif(!is_writeable(RUNTIME_PATH)) {  //判断目录是否可写，不可写报错并退出
        header('Content-Type:text/html; charset=utf-8');
        exit('目录 [ '.RUNTIME_PATH.' ] 不可写！');
    }
    if(!is_dir(LOG_PATH))mkdir(CACHE_PATH,0777);  // 模板缓存目录
    if(!is_dir(LOG_PATH))mkdir(LOG_PATH,0777);    // 日志目录
    if(!is_dir(TEMP_PATH))mkdir(TEMP_PATH,0777);	// 数据缓存目录
    if(!is_dir(DATA_PATH))mkdir(DATA_PATH,0777);	// 数据文件目录
    return true;
}

